# coding: utf-8

import pandas as pd
import numpy as np
import math
from scipy.stats import norm
from scipy import stats
from collections import Iterable
from pandas import Series
from math import sqrt
import logging
import DAO.stockDAO as stockDAO
logging.basicConfig(level=logging.INFO)

q= 250
Rh=0.035
Rf=0.03



# ### 累计收益率
def cumulative_return(date_line,return_line):
    """
    :param date_line: 日期序列
    :param return_line: 账户日收益率序列
    :param indexreturn_line: 基准日收益率序列
    :return: 输出账户和基准的累计日收益率序列
    """
    return_line=list(return_line)
    date_line=list(date_line)
    df = pd.DataFrame({'date': date_line, 'rtn': return_line})
    df['target_cumret']=(df['rtn']+1).cumprod()
    
    return float(df['target_cumret'].tail(1))


# ### 年化总收益率


def Annual_total_return(date_line,return_line,q=q):
    """
    :param date_line: 日期序列
    :param return_line: 账户日收益率序列
    :return: 输出周期化的收益率
    """
    T=len(date_line)
    r_tmp = np.array(return_line) + 1.0
    s = 1
    for x in r_tmp:
        s *= x
    r_annual = math.pow(s, q / T) - 1
    return r_annual




# ### 年化主动收益

# In[61]:


def Annual_active_return(date_line,return_line,indexreturn_line,q=q):
    """
    :param date_line: 日期序列
    :param return_line: 账户日收益率序列
    :param indexreturn_line: 基准日收益率序列
    :return: 输出周期化的主动收益率
    """
    T = len(date_line)
    Rp=Annual_total_return(date_line=date_line,return_line=return_line,q=q)
    Rbi=Annual_total_return(date_line=date_line,return_line=indexreturn_line,q=q)
    return Rp-Rbi




# ### 年化总风险

# In[63]:


def Annual_total_risk(date_line,return_line,q=q):
    """
    :param date_line: 日期序列
    :param return_line: 账户日收益率序列
    :return: 输出周期化的收益率
    """
    T=len(date_line)
    r_tmp = np.array(return_line)
    return math.pow((np.sum(np.power(return_line - np.mean(return_line), 2)) * (q / T)), 1 / 2) 



# ### 年化主动风险

# In[65]:


def Annual_active_risk(date_line,return_line,indexreturn_line,q=q):
    """
    :param date_line: 日期序列
    :param return_line: 账户日收益率序列
    :return: 输出年化主动风险
    """
    re=return_line-indexreturn_line
    re.dropna(inplace=True)
    return Annual_total_risk(date_line,re,q)


# In[66]:


# ### alpha,beta实现

# In[67]:


def alphabetalinearRegression(return_line,indexreturn_line,rf=Rf,q=q):
    """
    :rtype: (float,float)
    :param return_line: 账户日收益率序列
    :param indexreturn_line: 基准日收益序列
    :return: 输出beta,alpha
    """
    #•线性回归 Rpi - Rf = α + β(Rbi - Rf) + ε 
    rf_d = np.power((1 + rf ),1/q) -1
    from sklearn import datasets, linear_model
    from sklearn.metrics import mean_squared_error, r2_score
    Y = return_line - rf_d
    X = indexreturn_line - rf_d
    X_tmp = np.array(list(X))
    X_tmp = X_tmp.reshape(-1,1)

    Y_tmp = np.array(list(Y))
    Y_tmp = Y_tmp.reshape(-1,1)
    # Create linear regression object
    regr = linear_model.LinearRegression()

    # Train the model using the training sets
    result = regr.fit(X_tmp*100, Y_tmp*100)
    return {'alpha':result.coef_[0][0],'beta':result.intercept_[0]}


# In[68]:


def residual_term(return_line,indexreturn_line):
    """
    :param return_line: 账户日收益率序列
    :param indexreturn_line: 基准日收益序列
    :return: 输出残差余项风险
    """
    eps = return_line - indexreturn_line
    return eps.std()



# ### 信息比率 IR

# In[71]:


def IR(date_line,return_line,indexreturn_line,q=q):
    """
    :param return_line: 账户日收益率序列
    :param indexreturn_line: 基准日收益序列
    :return: 输出年化主动收益比年化主动风险
    """
    T=len(date_line)
    x=Annual_active_return(date_line,return_line,indexreturn_line,q)
    y=Annual_active_risk(date_line,return_line,indexreturn_line,q)
    return x/y


# ### 夏普比例

# In[73]:


def SharpRatio(date_line,return_line,Rf=Rf,q=q):
    """
    :param return_line: 账户日收益率序列
    :return: 输出超额收益率比年化总风险
    """
    x=Annual_total_return(date_line,return_line, q)-Rf
    y=Annual_total_risk(date_line,return_line,q)
    return x/y


# In[74]:



# In[75]:


##年化下行风险概率 
def LMPNA(r,Rh=Rh,n=2):
    """
    :param r: 账户年收益率序列
    :return: 输出年化下行风险
    """
    #选取小于最小可接受风险值的序列，其他忽略
    lst_tmp=np.array(r.copy())-Rh
    lst_tmp[lst_tmp > 0] = 0
    t=len(lst_tmp[lst_tmp<0])
    return math.pow(np.sum(np.power(lst_tmp, n))/(t-1),1/n)

#使用年收益率序列的Sortino算法
def SortinoRatio1(rp,rh,year_return_line):
        lmp=LMPNA(year_return_line,rh)
        return (rp-rh)/lmp


# In[76]:

#下行风险概率
def LMPN(date_line,return_line,Rf=Rf,q=q,n=2):
    """
    :param date_line: 日期序列
    :param return_line: 账户日收益率序列
    :return: 输出年化下行风险
    """
    T=len(date_line)
    rf=np.power((1+Rf),1/q)-1
    lst_tmp = np.array(return_line.copy()) - rf
    # if return r1> Rh, ignore it
    lst_tmp[lst_tmp > 0] = 0
    t=np.size(lst_tmp[lst_tmp<0])
    return math.pow(np.sum(np.power(lst_tmp, n))*(q/T)/(t-1),1/n)


# In[77]:

#使用日收益率的索提诺序列
def SortinoRatio(date_line,return_line,Rf=Rf,q=q):
    """
    :param return_line: 账户日收益率序列
    :return: 输出超额收益率比年化总风险
    """
    Rp=Annual_total_return(date_line,return_line,q)
    lmp=LMPN(date_line,return_line,Rf,q)
    return (Rp-Rf)/lmp


# ### 年化波动率

# In[81]:


# 计算收益波动率的函数
def volatility(date_line, return_line,q=q):
    """
    :param date_line: 日期序列
    :param return_line: 账户日收益率序列
    :return: 输出回测期间的收益波动率
    """
    from math import sqrt
    df = pd.DataFrame({'date': date_line, 'rtn': return_line})
    # 计算波动率
    vol = df['rtn'].std() * sqrt(q)
    return vol


# ### 最大回撤

# In[82]:


def Max_Drawdown_withDate(date_line,capital_line):
    """
    :param date_line: 日期序列
    :param capital_line: 账户日收益率序列
    :return: 输出最大回撤及其开始，结束日期
    """
    df = pd.DataFrame({'date': date_line, 'capital': capital_line})
    df['date']=pd.to_datetime(df['date'])
    df.sort_values(by='date',inplace=True)
    df.reset_index(drop=True,inplace=True)
    
    df['max2here']=df['capital'].expanding(min_periods=1).max()
    df['dd2here']=df['capital']/df['max2here']-1
    
    #计算最大回撤和结束时间
    tmp = df.sort_values(by='dd2here').iloc[0][['date','dd2here']]
    maxdd = tmp['dd2here']
    end_date = tmp['date']
    #计算开始时间
    df = df[df['date'] <= end_date]
    start_date = df.sort_values(by='capital', ascending=False).iloc[0]['date']
    #print('最大回撤为：%f, 开始于:%s, 结束于:%s, 共:%s '%(maxdd,start_date,end_date,end_date-start_date))
    return {'最大回撤':maxdd,'开始日期':str(start_date), '结束日期':str(end_date)}





# ### 最大连涨期数和 最小连涨期数

# In[84]:


# 计算最大连续上涨天数和最大连续下跌天数
def Max_consecutive_up_days(date_line, return_line):
    """
    :param date_line: 日期序列
    :param return_line: 账户日收益率序列
    :return: 输出最大连续上涨天数和最大连续下跌天数
    """
    df = pd.DataFrame({'date': date_line, 'rtn': return_line})
    # 新建一个全为空值的series,并作为dataframe新的一列
    s = pd.Series(np.nan, index=df.index)
    s.name = 'up'
    df = pd.concat([df, s], axis=1)

    # 当收益率大于0时，up取1，小于0时，up取0，等于0时采用前向差值
    df.ix[df['rtn'] > 0, 'up'] = 1
    df.ix[df['rtn'] < 0, 'up'] = 0
    df['up'].fillna(method='ffill', inplace=True)

    # 根据up这一列计算到某天为止连续上涨下跌的天数
    rtn_list = list(df['up'])
    successive_up_list = []
    num = 1
    for i in range(len(rtn_list)):
        if i == 0:
            successive_up_list.append(num)
        else:
            if (rtn_list[i] == rtn_list[i - 1] == 1) or (rtn_list[i] == rtn_list[i - 1] == 0):
                num += 1
            else:
                num = 1
            successive_up_list.append(num)
    # 将计算结果赋给新的一列'successive_up'
    df['successive_up'] = successive_up_list
    # 分别在上涨和下跌的两个dataframe里按照'successive_up'的值排序并取最大值
    max_successive_up = df[df['up'] == 1].sort_values(by='successive_up', ascending=False)['successive_up'].iloc[0]
    max_successive_down = df[df['up'] == 0].sort_values(by='successive_up', ascending=False)['successive_up'].iloc[0]
    #int强制转换避免int64无法json化
    return {'最大连续上涨天数':int(max_successive_up) , '最大连续下跌天数':int(max_successive_down)}


# ### 最大连涨幅度与最大连跌幅度

# In[85]:


# 计算最大连续上涨的幅度和最大连续下跌的幅度
def Max_consecutive_up_peak(date_line, return_line,capital_line):
    """
    :param date_line: 日期序列
    :param return_line: 账户日收益率序列
    :param capital_line: 账户日净值序列
    :return: 输出最大连续上涨幅度和最大连续下跌幅度
    """
    df = pd.DataFrame({'date': date_line, 'rtn': return_line,'cap':capital_line})
    # 新建一个全为空值的series,并作为dataframe新的一列
    s = pd.Series(np.nan, index=df.index)
    s.name = 'up'
    df = pd.concat([df, s], axis=1)

    # 当收益率大于0时，up取1，小于0时，up取0，等于0时采用前向差值
    df.ix[df['rtn'] > 0, 'up'] = 1
    df.ix[df['rtn'] < 0, 'up'] = 0
    df['up'].fillna(method='ffill', inplace=True)
    df['up'].iloc[0]=1#令第一条记录up=1，方便计算
    
    # 根据up这一列计算到某天为止的最大变化幅度
    rtn_list = list(df['up'])
    profit_up_list=[]
    base_cap=1 #记录一段连续变化的序列，其最初记录的净值
    for i in range(len(rtn_list)):
        if i == 0:#
            base_cap=df['cap'].iloc[i]
            profit_up_list.append(df['cap'].iloc[i]/base_cap-1) #涨幅跌幅记录
            #profit_up_list.append(base_cap)
        else:
            if (rtn_list[i] != rtn_list[i - 1] ):
                base_cap=df['cap'].iloc[i-1]
            profit_up_list.append(df['cap'].iloc[i]/base_cap-1)
    # 将计算涨幅变化结果赋给新的一列'profit_up'
    df['profit_up']=profit_up_list
    #计算收益最大值与最小值
    max_concecutive_up=df['profit_up'].max()
    max_concecutive_down=df['profit_up'].min()
    #int强制转换避免int64无法json化
    return {'最大连续涨幅':max_concecutive_up, '最大连续跌幅':max_concecutive_down}


# ### 盈利亏损期数占比

# In[86]:


def profit_period(date_line, return_line):
    """
    :param date_line: 日期序列
    :param return_line: 账户日收益率序列
    :return: 输出盈利期数和占总期数比例，亏损期数及占亏损比例
    """
    df = pd.DataFrame({'date': date_line, 'rtn': return_line})   
    #计算盈利期数
    totalperiod=len(df['rtn'])
    profitdaylen=len(df[df['rtn'] > 0])
    lossperiodlen=len(df[df['rtn'] < 0])
    return {'盈利天数':profitdaylen, '盈利期占比':profitdaylen/totalperiod,'亏损期天数':lossperiodlen, '亏损期占比':lossperiodlen/totalperiod}


# ### 峰度，偏度

# In[87]:


def Skew(return_line):
    """
    :param return_line: 账户日收益率序列
    :return: 输出收益率序列的偏度
    """
    data=return_line.copy()
    return data.skew()


# In[88]:


def Kurt(return_line):
    """
    :param return_line: 账户日收益率序列
    :return: 输出收益率序列的峰度
    """
    data=return_line.copy()
    return data.kurt()


# ### VAR 最大在险价值

# In[89]:


#历史模拟法，取最近100天净值记录，置信区间5%，取收益最低的5%记录的第五条做为目标
def varHistory(capital_line,a,N=100):
    """
    :param capital_line: 账户日净值序列
    :param a:置信区间可信度 如95%，a=95%=0.95
    :param curNet:计算日当日净值
    :return: 输出最大在险价值占当日净值比率
    """
    curNet=float(capital_line.tail(1))
    r = capital_line.tail(N)-curNet
    r=r.sort_values()
    r.plot.hist()
    index=round((1-a)*N)
    rvar=r.head(index).tail(1);
    return rvar[0]/curNet


# In[90]:





# ### 持股集中度 平均规模 平均持股数

# In[91]:


def stockHoldingDegree(position_data):
    """
    :param position_data: 账户持仓净值期序列
    :return: 输出持股集中度。每期持股前十名的持仓总和的平均
    """
    data=position_data.groupby(['year','period']).head(10)
    data=data.groupby(['year','period']).sum().mean()
    return data


# In[92]:




# In[93]:


def stockAverageCapital(position_data):
    """
    :param position_data: 账户持仓市值，期序列
    :return: 输出持股平均规模
    """
    data=position_data.groupby(['year','period']).sum().mean()
    return data

# In[95]:


def stockAverageVolumn(position_data):
    """
    :param position_data: 账户持仓数量，期序列
    :return: 输出持股平均数量
    """
    data=position_data.groupby(['year','period']).sum().mean()
    return data


# In[96]:




# ### 年化转手率

# In[97]:


def AnnualTurnOver(position_data):
    """
    :param position_data: 账户持仓数量，期序列
    :return: 输出持股平均数量
    """
    data=position_data.copy()
    #predf 上一期持仓记录
    cnt=0
    annualTurnOver=[]
    tradeAmounts=[]
    assetlist=[]
    tradeAmountsYear=[]
    copy=[]
    for year in yearlist:
        for period in periodlist:
            if(len(data.loc[year]['持仓市值（万元）'].loc[period])>0):
                if cnt<1 :
                    predf=data.loc[year]['持仓市值（万元）'].loc[period]
                    tradeAmount=predf.sum()
                    turnover=tradeAmount/predf.sum()
                    annualTurnOver.append(turnover)
                    tradeAmounts.append(tradeAmount)
                    copy.append(tradeAmount)
                    cnt=cnt+1
                    continue
                curdf=data.loc[year]['持仓市值（万元）'].loc[period]
                tmp=curdf.sub(predf,fill_value=0).apply(abs)
                tradeAmount=tmp.sum()
                turnover=tradeAmount/predf.sum()
                #print("year: "+year);print("period: "+period)
                #print(tradeAmount);print(predf.sum());print(turnover)
                annualTurnOver.append(turnover)
                tradeAmounts.append(tradeAmount)
                copy.append(tradeAmount)
                predf=curdf
                cnt=cnt+1
        sums=0;
        for ii in copy:
            sums=sums+ii
        tradeAmountsYear.append(sums)
        del copy[:]
    result=np.array(annualTurnOver);result=result.mean()
    tradeAmounts=np.array(tradeAmounts)
    assetlist=data.groupby(['year'])['持仓市值（万元）'].sum()
    yearTurnOverRate=(tradeAmountsYear/assetlist).mean()
    return result,yearTurnOverRate




def returnAllresult(date_line,capital_line,return_line,index_line,indexreturn_line):
    logging.info("quant calculating begins")
    result={}
    result['Annual_total_return']=Annual_total_return(date_line=date_line,return_line=return_line)
    result['cumulative_return']=cumulative_return(date_line=date_line,return_line=return_line)
    result['Annual_active_return']=Annual_active_return(date_line=date_line,return_line=return_line,indexreturn_line=indexreturn_line)
    result['Annual_active_risk']=Annual_active_risk(date_line=date_line,return_line=return_line,indexreturn_line=indexreturn_line)
    result['alpha,beta']=alphabetalinearRegression(return_line=return_line,indexreturn_line=indexreturn_line)
    result['residual_term']=residual_term(return_line,indexreturn_line)
    result['IR']=IR(date_line=date_line,return_line=return_line,indexreturn_line=indexreturn_line)
    result['SharpRatio']=SharpRatio(date_line=date_line,return_line=return_line,Rf=Rf)
    result['DownsideRisk_LMPN'] = LMPN(date_line,return_line,Rf=Rf)
    result['SortinoRatio']=SortinoRatio(date_line=date_line,return_line=return_line,Rf=Rf)
    result['varHistory,a=0.95']=varHistory(capital_line=capital_line,a=0.95)
    result['Max_Drawdown and recover date']=Max_Drawdown_withDate(date_line=date_line,capital_line=capital_line)
    result['volatility']=volatility(date_line=date_line,return_line=return_line)
    result['Max_consecutive_up_days']=Max_consecutive_up_days(date_line=date_line,return_line=return_line)
    result['Max_consecutive_up_peak']=Max_consecutive_up_peak(date_line=date_line,return_line=return_line,capital_line=capital_line)
    result['profit_period_ratio']=profit_period(date_line=date_line,return_line=return_line)
    result['Skew']=Skew(return_line=return_line)
    result['Kurt']=Kurt(return_line=return_line)
    logging.info("quant calculating ends")
    return result

